import { stdin, stdout } from 'process'
import { uuidv4 } from "./uuid"
import { TIMEOUTS } from './constant'

export class NativeMessagingHost {

    pendingRequests = new Map();

    // add message handler to wait for start server
    start() {
        try {
            this.setupMessageHandling();
        } catch (error) {
            process.exit(1);
        }
    }

    // 收到chrome-extension的消息
    setupMessageHandling() {
        let buffer = Buffer.alloc(0);
        let expectedLength = -1;

        stdin.on('readable', () => {
            let chunk = null;
            while ((chunk = stdin.read()) !== null) {
                buffer = Buffer.concat([buffer, chunk]);

                if (expectedLength === -1 && buffer.length >= 4) {
                    expectedLength = buffer.readUInt32LE(0);
                    buffer = buffer.subarray(4);
                }

                if (expectedLength !== -1 && buffer.length >= expectedLength) {
                    const messageBuffer = buffer.subarray(0, expectedLength);
                    buffer = buffer.subarray(expectedLength);

                    try {
                        const message = JSON.parse(messageBuffer.toString());
                        this.handleMessage(message);
                    } catch (error) {
                        this.sendMessage({
                            error: `Failed to parse message: ${error.message}`
                        });
                    }
                    expectedLength = -1; // reset to get next data
                }
            }
        });

        stdin.on('end', () => {
            this.cleanup();
        });

        stdin.on('error', () => {
            this.cleanup();
        });
    }

    async handleMessage(message) {
        if (!message || typeof message !== 'object') {
            this.sendMessage({
                error: 'Invalid message format'
            });
            return;
        }

        // 响应数据
        if (message.responseId) {
            const requestId = message.responseId;
            const pending = this.pendingRequests.get(requestId);

            if (pending) {
                clearTimeout(pending.timeoutId);
                if (message.error) {
                    pending.resolve({
                        error: message.error
                    });
                } else {
                    pending.resolve(message.payload);
                }
                this.pendingRequests.delete(requestId);
            }
            return;
        }

        if (message.payload?.type === 'ping_from_extension') {
            this.sendMessage({
                responseId: message.requestId,
                payload: {
                    type: 'pong_to_extension',
                }
            });
            return
        }
        //  有requestId响应
        if (message.requestId) {
            this.sendMessage({
                responseId: message.requestId,
                payload: {
                    message: 'host执行成功'
                }
            })
        }
    }

    /**
     * Send request to Chrome and wait for response
     * @param messagePayload Data to send to Chrome
     * @param timeoutMs Timeout for waiting response (milliseconds)
     * @returns Promise, resolves to Chrome's returned payload on success, rejects on failure
     */
    invokeMessage(payload, timeoutMs = TIMEOUTS.DEFAULT_REQUEST_TIMEOUT) {
        return new Promise((resolve, reject) => {
            const requestId = uuidv4();
            const timeoutId = setTimeout(() => {
                this.pendingRequests.delete(requestId);
                resolve({
                    error: `Host request timed out after ${timeoutMs}ms`
                });
            }, timeoutMs);

            this.pendingRequests.set(requestId, { resolve, reject, timeoutId });

            this.sendMessage({
                payload,
                requestId,
            });
        });
    }

    /**
     * Send message to Chrome extension
     */
    sendMessage(message) {
        if (!message || typeof message !== 'object') {
            throw Error('Invalid message format')
        }
        try {
            const messageString = JSON.stringify(message);
            const messageBuffer = Buffer.from(messageString);
            const headerBuffer = Buffer.alloc(4);
            headerBuffer.writeUInt32LE(messageBuffer.length, 0);
            // Ensure atomic write
            stdout.write(Buffer.concat([headerBuffer, messageBuffer]), (err) => {
                if (err) {
                    // Consider how to handle write failure, may affect request completion
                } else {
                    // Message sent successfully, no action needed
                }
            });
        } catch (error) {
            // Catch JSON.stringify or Buffer operation errors
            // If preparation stage fails, associated request may never be sent
            // Need to consider whether to reject corresponding Promise (if called within invokeMessage)
        }
    }



    /**
     * Clean up resources
     */
    cleanup() {
        // Reject all pending requests
        this.pendingRequests.forEach((pending) => {
            clearTimeout(pending.timeoutId);
            pending.reject(new Error('Native host is shutting down or Chrome disconnected.'));
        });
        this.pendingRequests.clear();
        process.exit(0);
    }
}

const nativeMessagingHostInstance = new NativeMessagingHost();
export { nativeMessagingHostInstance };


// 与浏览器拓展通过nativeMessage进行通信
// 与electron主进程通过tcp socket进行通信
// electron主进程与electron渲染进程通过IPC通信

// 实践要点与示例
// 无论选择哪种方式，在Electron中都需要遵循一个基本原则：​​与.exe程序的通信操作必须在主进程中进行​​。渲染进程（即你的UI界面）通过Electron的ipcMain和ipcRenderer模块与主进程通信，
// 再由主进程负责与外部.exe程序交互 。